Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / backend / http / ws / handlers_nomad.py

Displaying Raw • Download


meshchatx/src/backend/http/ws/handlers_nomad.py e41daa5b65e07a5baa1aaad32d97f90482f1cd1c (e41daa5b) Text, 22.19 KB

T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""WebSocket handlers: handlers_nomad."""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations


Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787httpT7ee787.T7ee787meshchat_names Tff7b72import Tb4b4b4( T8b949e# noqa: F401
Te6edf3GeoValidationErrorTb4b4b4,
Te6edf3OutboundHttpBlockedErrorTb4b4b4,
Te6edf3OverlayExportErrorTb4b4b4,
Te6edf3OverlaySourceParseErrorTb4b4b4,
Te6edf3PluginSecurityErrorTb4b4b4,
Te6edf3AsyncUtilsTb4b4b4,
Te6edf3InterfaceConfigParserTb4b4b4,
Te6edf3InterfaceDiscoveryTb4b4b4,
Te6edf3InterfaceEditorTb4b4b4,
Te6edf3LOGIN_PATHTb4b4b4,
Te6edf3LXMFTb4b4b4,
Te6edf3LxmfAudioFieldTb4b4b4,
Te6edf3LxmfFileAttachmentTb4b4b4,
Te6edf3LxmfFileAttachmentsFieldTb4b4b4,
Te6edf3LxmfImageFieldTb4b4b4,
Te6edf3MAX_EXPORT_TILESTb4b4b4,
Te6edf3MarkdownRendererTb4b4b4,
Te6edf3NomadnetFileDownloaderTb4b4b4,
Te6edf3NomadnetPageDownloaderTb4b4b4,
Te6edf3RNProbeHandlerTb4b4b4,
Te6edf3RNSTb4b4b4,
Te6edf3ReticulumMeshChatTb4b4b4,
Te6edf3SETUP_PATHTb4b4b4,
Te6edf3TRANSPARENT_TILETb4b4b4,
Te6edf3TelemeterTb4b4b4,
Te6edf3UTCTb4b4b4,
Te6edf3WSMsgTypeTb4b4b4,
Te6edf3_is_chaquopy_androidTb4b4b4,
Te6edf3_is_loopback_bind_hostTb4b4b4,
Te6edf3_request_client_ipTb4b4b4,
Te6edf3aiohttpTb4b4b4,
Te6edf3app_versionTb4b4b4,
Te6edf3assert_migration_context_pathsTb4b4b4,
Te6edf3asyncioTb4b4b4,
Te6edf3base64Tb4b4b4,
Te6edf3bcryptTb4b4b4,
Te6edf3binasciiTb4b4b4,
Te6edf3build_blocklist_export_documentTb4b4b4,
Te6edf3build_export_documentTb4b4b4,
Te6edf3build_messages_export_bundleTb4b4b4,
Te6edf3cache_statsTb4b4b4,
Te6edf3cancel_inbound_deliveriesTb4b4b4,
Te6edf3castTb4b4b4,
Te6edf3compute_lxmf_conversation_unread_from_latest_rowTb4b4b4,
Te6edf3configparserTb4b4b4,
Te6edf3contextlibTb4b4b4,
Te6edf3convert_db_favourite_to_dictTb4b4b4,
Te6edf3convert_db_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_nomadnet_field_data_to_mapTb4b4b4,
Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4,
Te6edf3convert_propagation_node_state_to_stringTb4b4b4,
Te6edf3copyTb4b4b4,
Te6edf3datetimeTb4b4b4,
Te6edf3describe_port_conflictTb4b4b4,
Te6edf3detect_image_format_from_magicTb4b4b4,
Te6edf3ensure_outbound_http_allowedTb4b4b4,
Te6edf3ensure_session_csrf_tokenTb4b4b4,
Te6edf3filter_announced_dicts_by_search_queryTb4b4b4,
Te6edf3fresh_storage_at_targetTb4b4b4,
Te6edf3get_cached_active_linkTb4b4b4,
Te6edf3get_file_pathTb4b4b4,
Te6edf3get_sessionTb4b4b4,
Te6edf3get_trusted_proxy_cidrsTb4b4b4,
Te6edf3gif_utilsTb4b4b4,
Te6edf3i2p_supportTb4b4b4,
Te6edf3import_messages_export_bundleTb4b4b4,
Te6edf3ioTb4b4b4,
Te6edf3is_mbtiles_filenameTb4b4b4,
Te6edf3is_path_within_dirTb4b4b4,
Te6edf3is_port_in_useTb4b4b4,
Te6edf3is_user_facing_lxmf_payloadTb4b4b4,
Te6edf3jsonTb4b4b4,
Te6edf3list_host_network_interfacesTb4b4b4,
Te6edf3list_inbound_deliveriesTb4b4b4,
Te6edf3list_portsTb4b4b4,
Te6edf3load_app_security_settingsTb4b4b4,
Te6edf3loggerTb4b4b4,
Te6edf3loggingTb4b4b4,
Te6edf3lxmf_sidebar_preview_for_conversation_latest_rowTb4b4b4,
Te6edf3memory_log_handlerTb4b4b4,
Te6edf3message_fields_have_attachmentsTb4b4b4,
Te6edf3migrate_legacy_to_targetTb4b4b4,
Te6edf3mime_for_image_typeTb4b4b4,
Te6edf3normalize_identity_storage_hashTb4b4b4,
Te6edf3normalize_lxmf_sieve_filtersTb4b4b4,
Te6edf3normalize_message_blocklistTb4b4b4,
Te6edf3osTb4b4b4,
Te6edf3parse_bool_query_paramTb4b4b4,
Te6edf3parse_import_documentTb4b4b4,
Te6edf3parse_lxmf_display_nameTb4b4b4,
Te6edf3parse_lxmf_propagation_node_app_dataTb4b4b4,
Te6edf3parse_lxmf_sieve_filters_jsonTb4b4b4,
Te6edf3parse_lxmf_stamp_costTb4b4b4,
Te6edf3parse_message_blocklist_jsonTb4b4b4,
Te6edf3parse_nomadnetwork_node_display_nameTb4b4b4,
Te6edf3platformTb4b4b4,
Te6edf3privacy_mode_enabledTb4b4b4,
Te6edf3psutilTb4b4b4,
Te6edf3purge_messages_before_cutoffTb4b4b4,
Te6edf3reTb4b4b4,
Te6edf3resolve_message_age_cutoffTb4b4b4,
Te6edf3reticulum_pathfindingTb4b4b4,
Te6edf3rotate_session_csrf_tokenTb4b4b4,
Te6edf3rrc_protocolTb4b4b4,
Te6edf3safe_path_under_dirTb4b4b4,
Te6edf3sanitize_sticker_emojiTb4b4b4,
Te6edf3sanitize_sticker_nameTb4b4b4,
Te6edf3sanitize_websocket_config_updateTb4b4b4,
Te6edf3save_app_security_settingsTb4b4b4,
Te6edf3secretsTb4b4b4,
Te6edf3shutilTb4b4b4,
Te6edf3sqlite3Tb4b4b4,
Te6edf3sticker_pack_utilsTb4b4b4,
Te6edf3sysTb4b4b4,
Te6edf3tempfileTb4b4b4,
Te6edf3threadingTb4b4b4,
Te6edf3timeTb4b4b4,
Te6edf3tracebackTb4b4b4,
Te6edf3user_agent_hashTb4b4b4,
Te6edf3validate_export_documentTb4b4b4,
Te6edf3webTb4b4b4,
Te6edf3websocket_type_requires_authTb4b4b4,
Te6edf3zipfileTb4b4b4,
Tb4b4b4)


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_download_cancelTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e# get data from websocket client
Te6edf3download_id Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3download_id Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return

T8b949e# cancel the download
Tff7b72if Te6edf3download_id Tff7b72in Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4:
Te6edf3downloader Tff7b72= Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]
Te6edf3downloaderTff7b72.Td2a8ffcancelTb4b4b4(Tb4b4b4)
Tff7b72del Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]

T8b949e# notify client
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.download.cancelledTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle getting page archives


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_page_archives_getTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3destination_hash Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4)
Te6edf3page_path Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3destination_hash Tff7b72or Tff7b72not Te6edf3page_pathTb4b4b4:
Tff7b72return

T8b949e# Try relative path first
Te6edf3archives Tff7b72= Te6edf3appTff7b72.Td2a8ffget_archived_page_versionsTb4b4b4(Te6edf3destination_hashTb4b4b4, Te6edf3page_pathTb4b4b4)

T8b949e# If nothing found and path doesn't look like it's already absolute,
T8b949e# try searching with the destination hash prefix (support for old buggy archives)
Tff7b72if Tff7b72not Te6edf3archives Tff7b72and Tff7b72not Te6edf3page_pathTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3buggy_path Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3destination_hashTffd700}Ta5d6ff:Tffd700{Te6edf3page_pathTffd700}Ta5d6ff"
Te6edf3archives Tff7b72= Te6edf3appTff7b72.Td2a8ffget_archived_page_versionsTb4b4b4(Te6edf3destination_hashTb4b4b4, Te6edf3buggy_pathTb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.archivesTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Ta5d6ff"Ta5d6ffarchivesTa5d6ff"Tb4b4b4: Tb4b4b4[
Tb4b4b4{
Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4, Ta5d6ff"Ta5d6ffisoformatTa5d6ff"Tb4b4b4)
Tff7b72else Tffa657strTb4b4b4(Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72for Te6edf3archive Tff7b72in Te6edf3archives
Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle loading a specific archived page version


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_page_archive_loadTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3archive_id Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffarchive_idTa5d6ff"Tb4b4b4)
Te6edf3download_id Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3archive_id Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffailureTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffailure_reasonTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffmissing archive_idTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return

Te6edf3archive Tff7b72= Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_archived_page_by_idTb4b4b4(Te6edf3archive_idTb4b4b4)

Tff7b72if Te6edf3archiveTb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsuccessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_contentTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffis_archived_versionTa5d6ff"Tb4b4b4: Tff7b72TrueTb4b4b4,
Ta5d6ff"Ta5d6ffarchived_atTa5d6ff"Tb4b4b4: Te6edf3archiveTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffailureTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffailure_reasonTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffarchive not foundTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle flushing all archived pages


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_page_archive_flushTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffflush_all_archived_pagesTb4b4b4(Tb4b4b4)
T8b949e# notify config updated
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(Te6edf3appTff7b72.Td2a8ffsend_config_to_websocket_clientsTb4b4b4(Tb4b4b4)Tb4b4b4)

T8b949e# handle manual page archiving


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_page_archive_addTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3destination_hash Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4)
Te6edf3page_path Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4)
Te6edf3content Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3destination_hash Tff7b72or Tff7b72not Te6edf3page_path Tff7b72or Tff7b72not Te6edf3contentTb4b4b4:
Tff7b72return

Te6edf3appTff7b72.Td2a8ffarchive_pageTb4b4b4(Te6edf3destination_hashTb4b4b4, Te6edf3page_pathTb4b4b4, Te6edf3contentTb4b4b4, Te6edf3is_manualTff7b72=Tff7b72TrueTb4b4b4)

T8b949e# notify client that page was archived
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.archive.addedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle downloading a file from a nomadnet node


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_file_downloadTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e# get data from websocket client
Te6edf3download_data Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3download_dataTb4b4b4:
Tff7b72return

Te6edf3destination_hash_hex Tff7b72= Te6edf3download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4)
Te6edf3file_path Tff7b72= Te6edf3download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4)
Te6edf3request_data Tff7b72= Te6edf3download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4)
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3request_dataTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3request_data Tff7b72= Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4(Te6edf3request_dataTb4b4b4)
Tff7b72elif Te6edf3request_data Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3request_data Tff7b72= Tb4b4b4{Tb4b4b4}

Tff7b72if Tff7b72not Te6edf3destination_hash_hex Tff7b72or Tff7b72not Te6edf3file_pathTb4b4b4:
Tff7b72return

Tff7b72tryTb4b4b4:
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hash_hexTb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Tff7b72return

Te6edf3local_file Tff7b72= Te6edf3appTff7b72.Td2a8ff_try_serve_local_page_node_fileTb4b4b4(
Te6edf3destination_hashTb4b4b4,
Te6edf3file_pathTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3local_file Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3file_nameTb4b4b4, Te6edf3file_bytes Tff7b72= Te6edf3local_file
Te6edf3appTff7b72.Td2a8ffdownload_id_counter Tff7b72+Tff7b72= T79c0ff1
Te6edf3download_id Tff7b72= Te6edf3appTff7b72.Td2a8ffdownload_id_counter
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsuccessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Ta5d6ff"Ta5d6fffile_nameTa5d6ff"Tb4b4b4: Te6edf3file_nameTb4b4b4,
Ta5d6ff"Ta5d6fffile_bytesTa5d6ff"Tb4b4b4: Te6edf3base64Tff7b72.Td2a8ffb64encodeTb4b4b4(Te6edf3file_bytesTb4b4b4)Tff7b72.Td2a8ffdecodeTb4b4b4(
Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return

T8b949e# generate download id
Te6edf3appTff7b72.Td2a8ffdownload_id_counter Tff7b72+Tff7b72= T79c0ff1
Te6edf3download_id Tff7b72= Te6edf3appTff7b72.Td2a8ffdownload_id_counter

T8b949e# handle successful file download
Tff7b72def Td2a8ffon_file_download_successTb4b4b4(Te6edf3file_nameTb4b4b4, Te6edf3file_bytesTb4b4b4)Tb4b4b4:
T8b949e# remove from active downloads
Tff7b72if Te6edf3download_id Tff7b72in Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4:
Tff7b72del Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]

T8b949e# Track download speed
Te6edf3download_size Tff7b72= Tffa657lenTb4b4b4(Te6edf3file_bytesTb4b4b4)
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3downloaderTb4b4b4, Ta5d6ff"Ta5d6ffstart_timeTa5d6ff"Tb4b4b4) Tff7b72and Te6edf3downloaderTff7b72.Td2a8ffstart_timeTb4b4b4:
Te6edf3download_duration Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3downloaderTff7b72.Td2a8ffstart_time
Tff7b72if Te6edf3download_duration Tff7b72> T79c0ff0Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffdownload_speedsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4(Te6edf3download_sizeTb4b4b4, Te6edf3download_durationTb4b4b4)Tb4b4b4)
T8b949e# Keep only last 100 downloads for average calculation
Tff7b72if Tffa657lenTb4b4b4(Te6edf3appTff7b72.Td2a8ffdownload_speedsTb4b4b4) Tff7b72> T79c0ff100Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffdownload_speedsTff7b72.Td2a8ffpopTb4b4b4(T79c0ff0Tb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsuccessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Ta5d6ff"Ta5d6fffile_nameTa5d6ff"Tb4b4b4: Te6edf3file_nameTb4b4b4,
Ta5d6ff"Ta5d6fffile_bytesTa5d6ff"Tb4b4b4: Te6edf3base64Tff7b72.Td2a8ffb64encodeTb4b4b4(Te6edf3file_bytesTb4b4b4)Tff7b72.Td2a8ffdecodeTb4b4b4(
Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle file download failure
Tff7b72def Td2a8ffon_file_download_failureTb4b4b4(Te6edf3failure_reasonTb4b4b4)Tb4b4b4:
T8b949e# remove from active downloads
Tff7b72if Te6edf3download_id Tff7b72in Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4:
Tff7b72del Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffailureTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffailure_reasonTa5d6ff"Tb4b4b4: Te6edf3failure_reasonTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle file download progress
Tff7b72def Td2a8ffon_file_download_progressTb4b4b4(Te6edf3progressTb4b4b4)Tb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: Te6edf3progressTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8ffon_file_download_phaseTb4b4b4(Te6edf3phaseTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffphaseTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffload_phaseTa5d6ff"Tb4b4b4: Te6edf3phaseTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# download the file
Te6edf3downloader Tff7b72= Te6edf3NomadnetFileDownloaderTb4b4b4(
Te6edf3destination_hashTb4b4b4,
Te6edf3file_pathTb4b4b4,
Te6edf3on_file_download_successTb4b4b4,
Te6edf3on_file_download_failureTb4b4b4,
Te6edf3on_file_download_progressTb4b4b4,
Te6edf3dataTff7b72=Te6edf3request_dataTb4b4b4,
Te6edf3on_phaseTff7b72=Te6edf3on_file_download_phaseTb4b4b4,
Te6edf3reticulumTff7b72=Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3downloaderTff7b72.Td2a8ffstart_time Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4] Tff7b72= Te6edf3downloader

T8b949e# notify client download started (await so phase updates cannot reorder ahead of started)
Tff7b72await Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_file_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffstartedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fffile_pathTa5d6ff"Tb4b4b4: Te6edf3file_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(Te6edf3downloaderTff7b72.Td2a8ffdownloadTb4b4b4(Tb4b4b4)Tb4b4b4)

T8b949e# handle downloading a page from a nomadnet node


Tff7b72async Tff7b72def Td2a8ffhandle_nomadnet_page_downloadTb4b4b4(Te6edf3appTb4b4b4, Te6edf3clientTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e# get data from websocket client
Te6edf3page_download_data Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3page_download_dataTb4b4b4:
Tff7b72return

Te6edf3destination_hash Tff7b72= Te6edf3page_download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4)
Te6edf3page_path Tff7b72= Te6edf3page_download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4)
Te6edf3field_data Tff7b72= Te6edf3page_download_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffield_dataTa5d6ff"Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3destination_hash Tff7b72or Tff7b72not Te6edf3page_pathTb4b4b4:
Tff7b72return

T8b949e# generate download id
Te6edf3appTff7b72.Td2a8ffdownload_id_counter Tff7b72+Tff7b72= T79c0ff1
Te6edf3download_id Tff7b72= Te6edf3appTff7b72.Td2a8ffdownload_id_counter

Te6edf3combined_data Tff7b72= Tb4b4b4{Tb4b4b4}
T8b949e# parse data from page path
T8b949e# example path then backtick then field1=123|field2=456
Te6edf3page_data Tff7b72= Tff7b72None
Te6edf3page_path_to_download Tff7b72= Te6edf3page_path
Tff7b72if Ta5d6ff"Ta5d6ff`Ta5d6ff" Tff7b72in Te6edf3page_pathTb4b4b4:
Te6edf3page_path_parts Tff7b72= Te6edf3page_pathTff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ff`Ta5d6ff"Tb4b4b4)
Te6edf3page_path_to_download Tff7b72= Te6edf3page_path_partsTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3page_data Tff7b72= Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4(Te6edf3page_path_partsTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)

T8b949e# Field data
Te6edf3field_data Tff7b72= Te6edf3convert_nomadnet_field_data_to_mapTb4b4b4(Te6edf3field_dataTb4b4b4)

T8b949e# Combine page data and field data
Tff7b72if Te6edf3page_data Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3combined_dataTff7b72.Td2a8ffupdateTb4b4b4(Te6edf3page_dataTb4b4b4)
Tff7b72if Te6edf3field_data Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3combined_dataTff7b72.Td2a8ffupdateTb4b4b4(Te6edf3field_dataTb4b4b4)

T8b949e# convert destination hash to bytes
Tff7b72tryTb4b4b4:
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72return

Te6edf3local_page Tff7b72= Te6edf3appTff7b72.Td2a8ff_try_serve_local_page_nodeTb4b4b4(
Te6edf3destination_hashTb4b4b4,
Te6edf3page_path_to_downloadTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3local_page Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3appTff7b72.Td2a8ffarchive_pageTb4b4b4(Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3page_pathTb4b4b4, Te6edf3local_pageTb4b4b4)
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsuccessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Ta5d6ff"Ta5d6ffpage_contentTa5d6ff"Tb4b4b4: Te6edf3local_pageTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return

T8b949e# handle successful page download
Tff7b72def Td2a8ffon_page_download_successTb4b4b4(Te6edf3page_contentTb4b4b4)Tb4b4b4:
T8b949e# remove from active downloads
Tff7b72if Te6edf3download_id Tff7b72in Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4:
Tff7b72del Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]

T8b949e# archive the page if enabled
Te6edf3appTff7b72.Td2a8ffarchive_pageTb4b4b4(Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3page_pathTb4b4b4, Te6edf3page_contentTb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffsuccessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Ta5d6ff"Ta5d6ffpage_contentTa5d6ff"Tb4b4b4: Te6edf3page_contentTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle page download failure
Tff7b72def Td2a8ffon_page_download_failureTb4b4b4(Te6edf3failure_reasonTb4b4b4)Tb4b4b4:
T8b949e# remove from active downloads
Tff7b72if Te6edf3download_id Tff7b72in Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4:
Tff7b72del Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4]

T8b949e# check if there are any archived versions
Te6edf3has_archives Tff7b72= Tb4b4b4(
Tffa657lenTb4b4b4(
Te6edf3appTff7b72.Td2a8ffget_archived_page_versionsTb4b4b4(
Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3page_pathTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72> T79c0ff0
Tb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffailureTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffailure_reasonTa5d6ff"Tb4b4b4: Te6edf3failure_reasonTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Ta5d6ff"Ta5d6ffhas_archivesTa5d6ff"Tb4b4b4: Te6edf3has_archivesTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# handle page download progress
Tff7b72def Td2a8ffon_page_download_progressTb4b4b4(Te6edf3progressTb4b4b4)Tb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: Te6edf3progressTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8ffon_page_download_phaseTb4b4b4(Te6edf3phaseTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(
Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffphaseTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffload_phaseTa5d6ff"Tb4b4b4: Te6edf3phaseTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

T8b949e# download the page
Te6edf3downloader Tff7b72= Te6edf3NomadnetPageDownloaderTb4b4b4(
Te6edf3destination_hashTb4b4b4,
Te6edf3page_path_to_downloadTb4b4b4,
Te6edf3combined_dataTb4b4b4,
Te6edf3on_page_download_successTb4b4b4,
Te6edf3on_page_download_failureTb4b4b4,
Te6edf3on_page_download_progressTb4b4b4,
Te6edf3on_phaseTff7b72=Te6edf3on_page_download_phaseTb4b4b4,
Te6edf3reticulumTff7b72=Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffactive_downloadsTb4b4b4[Te6edf3download_idTb4b4b4] Tff7b72= Te6edf3downloader

T8b949e# notify client download started (await so phase updates cannot reorder ahead of started)
Tff7b72await Te6edf3clientTff7b72.Td2a8ffsend_strTb4b4b4(
Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdownload_idTa5d6ff"Tb4b4b4: Te6edf3download_idTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet_page_downloadTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffstartedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_pathTa5d6ff"Tb4b4b4: Te6edf3page_pathTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Te6edf3AsyncUtilsTff7b72.Td2a8ffrun_asyncTb4b4b4(Te6edf3downloaderTff7b72.Td2a8ffdownloadTb4b4b4(Tb4b4b4)Tb4b4b4)

T8b949e# handle lxmf forwarding rules


Te6edf3HANDLERS Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffnomadnet.download.cancelTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_download_cancelTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.page.archives.getTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_page_archives_getTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.page.archive.loadTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_page_archive_loadTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.page.archive.flushTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_page_archive_flushTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.page.archive.addTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_page_archive_addTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.file.downloadTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_file_downloadTb4b4b4,
Ta5d6ff"Ta5d6ffnomadnet.page.downloadTa5d6ff"Tb4b4b4: Te6edf3handle_nomadnet_page_downloadTb4b4b4,
Tb4b4b4}


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────